# Copyright (c) 2013, yuxinyong and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import _
from zelin_am.utils import *
from frappe.utils import flt
import operator

def execute(filters=None):		
	columns = get_columns(filters.get('item_code'), filters.get('summary_level') )
	data = get_data(filters)
	return columns, data

def get_columns(item_code, summary_level):
	columns = []
	if not item_code:
		columns.extend([
			{
				"label": _("Item Code"),
				"fieldtype": "Link",
				"fieldname": "item_code",
				"options":'Item',			
				"width": 120
			},
			{
				"label": _("Item Name"),				
				"fieldname": "item_name",				
				"width": 180
			}
		])
	if not summary_level:
		columns.extend([
			{
				"label": _("Date"),
				"fieldtype": "Date",
				"fieldname": "due_date",			
				"width": 100
			},
			{
				"label": _("DocStatus"),
				"fieldtype": "Data",
				"fieldname": "docstatus",
				"hidden":1,		
				"width": 60
			},
			{
				"label": _("Ref Doctype"),
				#"fieldtype": "Link",
				"fieldtype": "Data",
				"fieldname": "ref_doctype",
				#"options": "DocType",
				"hidden": 1,			
				"width": 100
			},
			{
				"label": _("Process Doctype"),
				"fieldtype": "Data",
				"fieldname": "process_doctype",			
				"width": 120
			},
			{
				"label": _("Ref Docname"),
				#"fieldtype": "Data",
				"fieldtype": "Dynamic Link",
				"fieldname": "ref_docname",
				"options": "ref_doctype",
				"width": 140
			},
			{
				"label": _("Supply/Demand"),
				"fieldtype": "Float",
				"fieldname": "qty",
				"width": 120
			},
			{
				"label": _("Available Qty"),
				"fieldtype": "Float",
				"fieldname": "available_qty",
				"width": 120
			},
			{
				"label": _("Stock UOM"),
				"fieldtype": "Link",
				"fieldtype": "Data",
				"fieldname": "stock_uom",
				#"options": "UOM",
				"width": 80
			},
			{
				"label": _("Sales Order"),
				"fieldtype": "Link",
				"fieldname": "sales_order",
				"options": "Sales Order",			
				"width": 140
			},
			{
				"label": _("Business Partner Type"),
				"fieldtype": "Data",
				#"fieldtype": "Link",
				"fieldname": "partner_type",
				#"options": "DocType",			
				"width": 90
			},
			{
				"label": _("Business Partner"),
				#"fieldtype": "Data",
				"fieldtype": "Dynamic Link",
				"fieldname": "partner",
				"options": "partner_type",
				"width": 140
			},
			{
				"label": _("Warehouse"),
				"fieldtype": "Link",
				"fieldname": "warehouse",
				"options":"Warehouse",
				#"hidden":1,
				"width": 120
			}		
		])
	else:
		if summary_level != 'Overall Total':
			columns.append({
				"label": _("%s" % summary_level),
				"fieldtype": "Data",
				"fieldname": "period",			
				"width": 120
			})

		columns.extend([
			{
				"label": _("Start Qty"),
				"fieldtype": "Float",
				"fieldname": "start_qty",
				"width": 120
			},				
			{
				"label": _("Requirements"),
				"fieldtype": "Float",
				"fieldname": "requirement_qty",
				"width": 120
			},
			{
				"label": _("Supply"),
				"fieldtype": "Float",
				"fieldname": "receive_qty",
				"width": 120
			},
			{
				"label": _("Available Qty"),
				"fieldtype": "Float",
				"fieldname": "available_qty",
				"width": 120
			}
		])
	return columns

def get_period(in_date, summary_level):
	if summary_level == 'Day':
		res = in_date
	elif summary_level == 'Week':
		week_tuple = in_date.isocalendar()
		res = _('W {0}/{1}').format(week_tuple[0],week_tuple[1])
	elif summary_level == 'Month':
		res = _('M {0}/{1}').format(in_date.year, in_date.month)
	else:
		res = _("Overall Total")
	return res
	
def get_data(filters):	
	company = filters.get('company')
	item_code = filters.get('item_code')
	warehouses = filters.get('warehouse')
	item_group = filters.get('item_group')
	planner = filters.get('planner')
	summary_level = filters.get('summary_level')
	only_negative_ending = filters.get('only_negative_ending')
	if not warehouses:
		warehouses =  frappe.get_all('Warehouse', filters={'company': company, 'mrp_exclude':0}, pluck='name')	

	fn = (
			(get_open_mr_as_forecast,20),
            (get_open_so,21),
            (get_open_wo_gi,22),
            (get_open_mr_gi,23),
            (get_open_subcontract_gi,24),
            (get_open_wo,11),
        	(get_open_po,12),
            (get_open_mr,13)
		)
	item_set = set({item_code}) if item_code else set() 
	if planner:
		planner_item_set = set(
			frappe.get_all('Item Default', filters={'planner': planner,'company':company}, pluck='parent'))
		item_set = item_set.intersection(planner_item_set) if item_set else planner_item_set
		if not item_set: return 
	if item_group:
		item_group_item_set = set(frappe.get_all('Item', filters={'item_group': item_group}, pluck='name'))
		item_set = item_set.intersection(item_group_item_set) if item_set else item_group_item_set
		if not item_set: return 
	item_code_list = list(item_set)

	combine_list = []
	for (f, sort_key) in fn:
		data_list = f(warehouses, item_code_list = item_code_list, sort_key = sort_key)
		combine_list.extend(data_list)
	combine_list.extend(get_open_plan_order(company, warehouses, item_code_list = item_code_list, sort_key = 14, mrp = filters.get('mrp')))
	combine_list.extend(get_open_plan_order_items(company, warehouses, item_code_list = item_code_list, sort_key = 25, mrp = filters.get('mrp')))		
	#sort by item_code, date, then sort key (supply:1 or demand:2)
	combine_list = sorted(combine_list, key=lambda k: (k['item_code'], k['due_date'], k['sort_key'],k['ref_docname']))		
	row_cnt = len(combine_list)
	last_item_code, last_due_date, running_availability = None, None, 0
	data,negative_item_codes = [], []
	last_idx = len(combine_list) - 1
	item_code_onhand_map = get_item_code_onhand_map(company)
	item_safety_stock_map = get_item_safety_stock_map(company)

	for idx, d in enumerate(combine_list):
		item_code, qty, date_ = (d.item_code, d.qty, d.due_date)
		d.item_code_filter = item_code
		if item_code != last_item_code:
			open_on_hand,stock_uom = get_item_code_onhand(item_code, item_code_onhand_map)
			safety_stock = item_safety_stock_map.get(item_code, 0)
			open_row = frappe._dict({
				#'due_date': frappe.utils.today(),
				'ref_doctype':'',
				'ref_docname':'',
				'process_doctype': _('Initial Stock'),
				'period': _('Initial Stock'),
				'item_code_filter':item_code,				
				'available_qty': open_on_hand,
				'stock_uom': stock_uom
			})
			if safety_stock:
				safety_stock_row = frappe._dict({
					'ref_doctype':'',
					'ref_docname':'',
					'process_doctype': _('Safety Stock'),
					'period': _('Safety Stock'),
					'item_code_filter':item_code,
					'qty': -safety_stock,				
					'available_qty': open_on_hand - safety_stock,
					'stock_uom': stock_uom
				})
			if not summary_level:
				data.append(open_row)
				if safety_stock: data.append(safety_stock_row)
			running_availability = open_on_hand - safety_stock
			requirement_qty = receive_qty = 0
			last_item_code = item_code	
		if summary_level:
			period = get_period(date_, summary_level)
			if qty > 0:
				requirement_qty += qty
			else:
				receive_qty += qty
			# addup and output per period change	
			if (idx == row_cnt - 1 or 
					( period != get_period(combine_list[idx+1]['due_date'], summary_level) or 
				  	item_code != combine_list[idx+1]['item_code'])
				):
				d.start_qty = running_availability
				running_availability += requirement_qty + receive_qty
				d.period,d.receive_qty,d.requirement_qty,d.available_qty = (
					period, receive_qty,requirement_qty,running_availability)
				data.append(d)
				requirement_qty = receive_qty = 0
		else:
			d.start_qty = running_availability
			running_availability += qty
			d['available_qty'] = running_availability			
			data.append(d)

		if idx >= last_idx or item_code != combine_list[idx+1].item_code:
			if only_negative_ending and d.available_qty < 0:
				negative_item_codes.append(item_code)

	if only_negative_ending:
		data =[d for d in data if d.item_code_filter in negative_item_codes]				
	return data

"""
from zelin_am.agile_manufacturing.report.stock_requirements_list.stock_requirements_list import *
filters =frappe._dict({'company':'frappedemo', 'summary_level':'Overall Total','only_negative_ending':1})
d, item_codes = get_data(filters)
"""